/*
 * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
 * http://www.gnu.org/licenses/lgpl-3.0.html
 *
 * $Revision: 11139 $
 * $Id: editormanager.cpp 11139 2017-08-12 22:37:55Z fuscated $
 * $HeadURL: http://svn.code.sf.net/p/codeblocks/code/branches/release-17.xx/src/sdk/editormanager.cpp $
 */

#include "sdk_precomp.h"

#ifndef CB_PRECOMP
    #include <wx/dir.h>
    #include <wx/menu.h>
    #include <wx/notebook.h>
    #include <wx/settings.h>
    #include <wx/splitter.h>
    #include <wx/xrc/xmlres.h>

    #include "cbeditor.h"
    #include "cbproject.h"
    #include "compiler.h"
    #include "compilerfactory.h"
    #include "configmanager.h"
    #include "editormanager.h" // class's header file
    #include "filemanager.h"
    #include "logmanager.h"
    #include "macrosmanager.h"
    #include "manager.h"
    #include "pluginmanager.h"
    #include "projectbuildtarget.h"
    #include "projectmanager.h"
    #include "sdk_events.h"
#endif

#include "annoyingdialog.h"
#include "cbstyledtextctrl.h"
#include "cbcolourmanager.h"

#include <wx/bmpbuttn.h>
#include <wx/progdlg.h>
#include <wx/tokenzr.h>

#include "cbauibook.h"
#include "editorcolourset.h"
#include "confirmreplacedlg.h"
#include "filefilters.h"
#include "projectfileoptionsdlg.h"
#include "filegroupsandmasks.h"

template<> EditorManager* Mgr<EditorManager>::instance = nullptr;
template<> bool  Mgr<EditorManager>::isShutdown = false;

int ID_NBEditorManager        = wxNewId();
int ID_EditorManager          = wxNewId();
int idEditorManagerCheckFiles = wxNewId();

// static
bool EditorManager::s_CanShutdown = true;
// needed for initialization of variables
// 变量初始化所需
inline int cbRegisterId(int id)
{
    wxRegisterId(id);
    return id;
}

static const int idNBTabSplitHorz            = wxNewId();
static const int idNBTabSplitVert            = wxNewId();
static const int idNBTabUnsplit              = wxNewId();
static const int idNBTabClose                = wxNewId();
static const int idNBTabCloseAll             = wxNewId();
static const int idNBTabCloseAllOthers       = wxNewId();
static const int idNBTabSave                 = wxNewId();
static const int idNBTabSaveAll              = wxNewId();
static const int idNBSwapHeaderSource        = wxNewId();
static const int idNBTabOpenContainingFolder = wxNewId();
static const int idNBTabTop                  = wxNewId();
static const int idNBTabBottom               = wxNewId();
static const int idNBProperties              = wxNewId();
static const int idNBAddFileToProject        = wxNewId();
static const int idNBRemoveFileFromProject   = wxNewId();
static const int idNBShowFileInTree          = wxNewId();

// The following lines reserve 255 consecutive id's
// 以下行保留255个连续的id
static const int EditorMaxSwitchTo           = 255;
static const int idNBSwitchFile1             = wxNewId();
static const int idNBSwitchFileMax           = cbRegisterId(idNBSwitchFile1 + EditorMaxSwitchTo - 1);


/** *******************************************************
  * struct EditorManagerInternalData                      *
  * This is the private data holder for the EditorManager *
  * All data not relevant to other classes should go here *
  *
  * 编辑器管理器内部数据结构体
  * 这是编辑器管理器(EditorManager)的私有数据持有者所有.
  * 与其他类无关的数据都应该放在这里
  ********************************************************* */

struct EditorManagerInternalData
{
    /* Methods */
    EditorManagerInternalData(EditorManager* owner): m_pOwner(owner){}
    /* Static data */
    EditorManager* m_pOwner;
    bool m_SetFocusFlag;
};

// *********** End of EditorManagerInternalData **********


BEGIN_EVENT_TABLE(EditorManager, wxEvtHandler)
    EVT_APP_STARTUP_DONE(EditorManager::OnAppDoneStartup)
    EVT_APP_START_SHUTDOWN(EditorManager::OnAppStartShutdown)
    EVT_AUINOTEBOOK_PAGE_CHANGED(ID_NBEditorManager, EditorManager::OnPageChanged)
    EVT_AUINOTEBOOK_PAGE_CHANGING(ID_NBEditorManager, EditorManager::OnPageChanging)
    EVT_AUINOTEBOOK_PAGE_CLOSE(ID_NBEditorManager, EditorManager::OnPageClose)
    EVT_AUINOTEBOOK_TAB_RIGHT_UP(ID_NBEditorManager, EditorManager::OnPageContextMenu)
    EVT_MENU_RANGE(idNBSwitchFile1, idNBSwitchFileMax, EditorManager::OnGenericContextMenuHandler)
    EVT_MENU(idNBTabSplitHorz, EditorManager::OnGenericContextMenuHandler)
    EVT_MENU(idNBTabSplitVert, EditorManager::OnGenericContextMenuHandler)
    EVT_MENU(idNBTabUnsplit, EditorManager::OnGenericContextMenuHandler)
    EVT_MENU(idNBTabTop, EditorManager::OnTabPosition)
    EVT_MENU(idNBTabBottom, EditorManager::OnTabPosition)
    EVT_MENU(idNBTabClose, EditorManager::OnClose)
    EVT_MENU(idNBTabCloseAll, EditorManager::OnCloseAll)
    EVT_MENU(idNBTabCloseAllOthers, EditorManager::OnCloseAllOthers)
    EVT_MENU(idNBTabOpenContainingFolder, EditorManager::OnOpenContainingFolder)
    EVT_MENU(idNBTabSave, EditorManager::OnSave)
    EVT_MENU(idNBTabSaveAll, EditorManager::OnSaveAll)
    EVT_MENU(idNBSwapHeaderSource, EditorManager::OnSwapHeaderSource)
    EVT_MENU(idNBProperties, EditorManager::OnProperties)
    EVT_MENU(idNBAddFileToProject, EditorManager::OnAddFileToProject)
    EVT_MENU(idNBRemoveFileFromProject, EditorManager::OnRemoveFileFromProject)
    EVT_MENU(idNBShowFileInTree, EditorManager::OnShowFileInTree)
    EVT_MENU(idEditorManagerCheckFiles, EditorManager::OnCheckForModifiedFiles)
END_EVENT_TABLE()

EditorManager::EditorManager()
        : m_pNotebook(nullptr),
        m_pNotebookStackHead(new cbNotebookStack),
        m_pNotebookStackTail(m_pNotebookStackHead),
        m_nNotebookStackSize(0),
        m_isCheckingForExternallyModifiedFiles(false)
{
    m_pData = new EditorManagerInternalData(this);
    m_pNotebook = new cbAuiNotebook(Manager::Get()->GetAppWindow(), ID_NBEditorManager, wxDefaultPosition, wxDefaultSize,
                                    wxAUI_NB_DEFAULT_STYLE | wxAUI_NB_WINDOWLIST_BUTTON | wxNO_FULL_REPAINT_ON_RESIZE | wxCLIP_CHILDREN);

    if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/editor_tabs_bottom"), false))
        m_pNotebook->SetWindowStyleFlag(m_pNotebook->GetWindowStyleFlag() | wxAUI_NB_BOTTOM);

    Manager::Get()->GetLogManager()->DebugLog(wxT("--13.1.7.5.1初始化编辑颜色集(配色方案) ....."));
    m_Theme = new EditorColourSet(Manager::Get()->GetConfigManager(_T("editor"))->Read(_T("/colour_sets/active_colour_set"), COLORSET_DEFAULT));
    Manager::Get()->GetLogManager()->DebugLog(wxT("--13.1.7.5.2初始化编辑颜色集(配色方案): 完成."));
    //推送编辑器管理事件(wxWidgets的事件机制)
    Manager::Get()->GetAppWindow()->PushEventHandler(this);
    /*缩放倍数*/
    m_Zoom = Manager::Get()->GetConfigManager(_T("editor"))->ReadInt(_T("/zoom"));
    /*注册事件接收器*/
    Manager::Get()->RegisterEventSink(cbEVT_BUILDTARGET_SELECTED, new cbEventFunctor<EditorManager, CodeBlocksEvent>(this, &EditorManager::CollectDefines));
    Manager::Get()->RegisterEventSink(cbEVT_PROJECT_ACTIVATE, new cbEventFunctor<EditorManager, CodeBlocksEvent>(this, &EditorManager::CollectDefines));
    Manager::Get()->RegisterEventSink(cbEVT_WORKSPACE_LOADING_COMPLETE, new cbEventFunctor<EditorManager, CodeBlocksEvent>(this, &EditorManager::CollectDefines));
    /*配色方案管理器*/
    ColourManager *colours = Manager::Get()->GetColourManager();
    colours->RegisterColour(wxT("编辑器"), wxT("插入符号"), wxT("editor_caret"), *wxBLACK);
    colours->RegisterColour(wxT("编辑器"), wxT("右边距"), wxT("editor_gutter"), *wxLIGHT_GREY);
    colours->RegisterColour(wxT("编辑器"), wxT("行号前景色"), wxT("editor_linenumbers_fg"),
                            wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
    colours->RegisterColour(wxT("编辑器"), wxT("行号背景色"), wxT("editor_linenumbers_bg"),
                            wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));

    // These two are taken from Platform::Chrome() and Platform::ChromeHightlight()
    // 这两个是从Platform::Chrome()和Platform::ChromeHightlight()取得的
    colours->RegisterColour(wxT("编辑器"), wxT("边缘镀铬(Margin chrome colour)"), wxT("editor_margin_chrome"),
                            wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
    colours->RegisterColour(wxT("编辑器"), wxT("边缘镀铬突出显示颜色"), wxT("editor_margin_chrome_highlight"),
                            wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT));
    colours->RegisterColour(wxT("编辑器"), wxT("空白"), wxT("editor_whitespace"), wxColor(195, 195, 195));
}

EditorManager::~EditorManager()
{
    DeleteNotebookStack();
    delete m_pNotebookStackHead;
    delete m_Theme;
    delete m_pData;
    Manager::Get()->GetConfigManager(_T("editor"))->Write(_T("/zoom"), m_Zoom);
}

cbNotebookStack* EditorManager::GetNotebookStack()
{
    bool found = false;
    wxWindow* wnd;
    cbNotebookStack* body;
    cbNotebookStack* prev_body;
    // Sync stack with Notebook
    /*同步笔记本空件堆*/
    while (m_nNotebookStackSize != m_pNotebook->GetPageCount())
    {
        if (m_nNotebookStackSize < m_pNotebook->GetPageCount())
        {
            for (size_t i = 0; i<m_pNotebook->GetPageCount(); ++i)
            {
                wnd = m_pNotebook->GetPage(i);
                found = false;
                for (body = m_pNotebookStackHead->next; body != NULL; body = body->next)
                {
                    if (wnd == body->window)
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    m_pNotebookStackTail->next = new cbNotebookStack(wnd);
                    m_pNotebookStackTail = m_pNotebookStackTail->next;
                    ++m_nNotebookStackSize;
                }
            }
        }
        if (m_nNotebookStackSize > m_pNotebook->GetPageCount())
        {
            for (prev_body = m_pNotebookStackHead, body = prev_body->next; body != NULL; prev_body = body, body = body->next)
            {
                if (m_pNotebook->GetPageIndex(body->window) == wxNOT_FOUND)
                {
                    prev_body->next = body->next;
                    delete body;
                    --m_nNotebookStackSize;
                    body = prev_body;
                }
            }
        }
    }

    return m_pNotebookStackHead->next;
}

void EditorManager::DeleteNotebookStack()
{
    cbNotebookStack* tmp;
    while(m_pNotebookStackHead->next)
    {
        tmp = m_pNotebookStackHead->next;
        m_pNotebookStackHead->next = tmp->next;
        delete tmp;
    }
    m_pNotebookStackTail = m_pNotebookStackHead;
    m_nNotebookStackSize = 0;
}

void EditorManager::RebuildNotebookStack()
{
    DeleteNotebookStack();
    for (size_t i = 0; i < m_pNotebook->GetPageCount(); ++i)
    {
        m_pNotebookStackTail->next = new cbNotebookStack(m_pNotebook->GetPage(i));
        m_pNotebookStackTail = m_pNotebookStackTail->next;
        ++m_nNotebookStackSize;
    }
}

void EditorManager::RecreateOpenEditorStyles()
{
    // tell all open editors to re-create their styles
    // 告诉所有打开的编辑器重新创建其样式
    for (size_t i = 0; i < m_pNotebook->GetPageCount(); ++i)
    {
        cbEditor* ed = InternalGetBuiltinEditor(i);
        if (ed)
        {
            //First Save the old fold levels
            //先保存旧的折叠层
            bool saveSuccess = ed->SaveFoldState();
            ed->SetEditorStyle();
            if (saveSuccess)
            {
                // Compare old fold levels with new and change the bugs
                // 比较新旧折叠级别并更改错误
                ed->FixFoldState();
            }
        }
    }
}

int EditorManager::GetEditorsCount()
{
    return m_pNotebook->GetPageCount();
}

EditorBase* EditorManager::InternalGetEditorBase(int page)
{
    if (page >= 0 && page < (int)m_pNotebook->GetPageCount())
    {
        return static_cast<EditorBase*>(m_pNotebook->GetPage(page));
    }
    return nullptr;
}

cbEditor* EditorManager::InternalGetBuiltinEditor(int page)
{
    EditorBase* eb = InternalGetEditorBase(page);
    if (eb && eb->IsBuiltinEditor())
        return (cbEditor*)eb;
    return nullptr;
}

cbEditor* EditorManager::GetBuiltinEditor(EditorBase* eb)
{
    return eb && eb->IsBuiltinEditor() ? (cbEditor*)eb : nullptr;
}

EditorBase* EditorManager::IsOpen(const wxString& filename)
{
    wxString uFilename = UnixFilename(realpath(filename));
    for (size_t i = 0; i < m_pNotebook->GetPageCount(); ++i)
    {
        EditorBase* eb = InternalGetEditorBase(i);
        if (!eb){continue;}
        wxString fname = eb->GetFilename();
        // MSW must use case-insensitive comparison
        // MSW必须使用不区分大小写的比较
        if (fname.IsSameAs(uFilename, platform::windows == false) || fname.IsSameAs(g_EditorModified + uFilename, platform::windows == false)){return eb;}
    }

    return NULL;
}

EditorBase* EditorManager::GetEditor(int index)
{
    return InternalGetEditorBase(index);
}

void EditorManager::SetColourSet(EditorColourSet* theme)
{
    if (m_Theme)
        delete m_Theme;

    // copy locally
    // 本地复制
    m_Theme = new EditorColourSet(*theme);

    for (size_t i = 0; i < m_pNotebook->GetPageCount(); ++i)
    {
        cbEditor* ed = InternalGetBuiltinEditor(i);
        if (ed)
            ed->SetColourSet(m_Theme);
    }
}

cbEditor* EditorManager::Open(const wxString& filename, int pos, ProjectFile* data)
{
    return Open(nullptr, filename, pos, data);
}

cbEditor* EditorManager::Open(LoaderBase* fileLdr, const wxString& filename, int /*pos*/, ProjectFile* data)
{
    bool can_updateui = !GetActiveEditor() || !Manager::Get()->GetProjectManager()->IsLoading();
    wxFileName fn(realpath(filename));
    NormalizePath(fn, wxEmptyString);
    wxString fname = UnixFilename(fn.GetFullPath());
    Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("正在尝试打开文件'%s'"), fname.wx_str()));
    if (!wxFileExists(fname)){return nullptr;}
    Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("文件'%s'已经存在"), fname.wx_str()));
    // disallow application shutdown while opening files
    // WARNING: remember to set it to true, when exiting this function!!!
    // 打开文件时不允许关闭应用程序
    // 警告：退出此函数时,请记住将其设置为true!!!
    s_CanShutdown = false;

    EditorBase* eb = IsOpen(fname);
    cbEditor* ed = nullptr;
    if (eb)
    {
        if (eb->IsBuiltinEditor())
        {
            ed = (cbEditor*)eb;
        }
        else
        {
            s_CanShutdown = true;
            // is open but not a builtin editor
            // 它是打开的,但不是内置的编辑器
            return nullptr;
        }
    }

    if (!ed)
    {
        if (!fileLdr){fileLdr = Manager::Get()->GetFileManager()->Load(filename);}
        if (fileLdr)
        {
            ed = new cbEditor(m_pNotebook, fileLdr, fname, m_Theme);
            if (ed->IsOK()) {
                AddEditorBase(ed);
            }else {
                ed->Destroy();
                ed = nullptr;
            }
        }
    }

    // check for ProjectFile
    // 检查项目文件
    if (ed && !ed->GetProjectFile()) {
        // First checks if we're already being passed a ProjectFile as a parameter
        // 首先检查是否已将项目文件作为参数传递
        if (data) {
            //Manager::Get()->GetLogManager()->DebugLog(wxT("项目数据集 ") + data->file.GetFullPath());
            Manager::Get()->GetLogManager()->DebugLog(wxT("项目数据集 ") + data->file.GetName());
        } else { Manager::Get()->GetProjectManager()->FindProjectForFile(ed->GetFilename(), &data, false, false); }
        if (data) { ed->SetProjectFile(data, true); }
    }

    if (can_updateui)
    {
        if (ed)
        {
            // fires the cbEVT_EDITOR_ACTIVATED event
            // 触发cbEVT_EDITOR_ACTIVATED事件
            SetActiveEditor(ed);
            ed->GetControl()->SetFocus();
        }
    }

    // we 're done
    // 完成
    s_CanShutdown = true;
    return ed;
}

EditorBase* EditorManager::GetActiveEditor()
{
    if (m_pNotebook->GetPageCount() > 0)
    {
        return InternalGetEditorBase(m_pNotebook->GetSelection());
    }
    return nullptr;
}

void EditorManager::ActivateNext()
{
    m_pNotebook->AdvanceSelection(true);
}

void EditorManager::ActivatePrevious()
{
    m_pNotebook->AdvanceSelection(false);
}

void EditorManager::SetActiveEditor(EditorBase* ed)
{
    if (!ed)
        return;
    int page = FindPageFromEditor(ed);
    if (page != -1)
    {
        // Previously the Activated event was only sent for built-in editors, which makes no sense
        // 以前,激活的事件只发送给内置的编辑器,这毫无意义
        int sel = m_pNotebook->GetSelection();
        m_pNotebook->SetSelection(page);
        EditorBase* eb_old = nullptr;
        if (sel>=0)
            eb_old = static_cast<EditorBase*>(m_pNotebook->GetPage(sel));

        CodeBlocksEvent evt(cbEVT_EDITOR_SWITCHED, -1, nullptr, ed, nullptr, eb_old);
        Manager::Get()->GetPluginManager()->NotifyPlugins(evt);

        CodeBlocksEvent evt2(cbEVT_EDITOR_ACTIVATED, -1, nullptr, ed);
        Manager::Get()->GetPluginManager()->NotifyPlugins(evt2);
    }

    if (ed->IsBuiltinEditor())
        static_cast<cbEditor*>(ed)->GetControl()->SetFocus();
}

/*新建编辑器*/
cbEditor* EditorManager::New(const wxString& newFileName)
{
    // wxString old_title = Manager::Get()->GetAppWindow()->GetTitle(); // Fix for Bug #1389450
    // create a dummy file
    // 创建一个转储文件
    if (!newFileName.IsEmpty() && !wxFileExists(newFileName) && wxDirExists(wxPathOnly(newFileName)))
    {
        wxFile f(newFileName, wxFile::write);
        if (!f.IsOpened())
            {return nullptr;}
    }
    cbEditor* ed = new cbEditor(m_pNotebook, newFileName);
//    if ((newFileName.IsEmpty() && !ed->SaveAs()) || !ed->Save())
//    {
//        //DeletePage(ed->GetPageIndex());
//        ed->Destroy();
// //Though I can't reproduce the bug, this does no harm
// //虽然我不能复制这种虫子,但这没有坏处
//        Manager::Get()->GetAppWindow()->SetTitle(old_title);
//        return 0;
//    }

    // add default text
    // 添加默认文本
    wxString key;
    key.Printf(_T("/default_code/set%d"), (int)FileTypeOf(ed->GetFilename()));
    wxString code = Manager::Get()->GetConfigManager(_T("editor"))->Read(key, wxEmptyString);
    // Allow usage of macros
    // 允许使用宏
    // TODO (Morten#5#): Is it worth making this configurable?!
    //  TODO:这值得配置吗?!
    Manager::Get()->GetMacrosManager()->ReplaceMacros(code);
    ed->GetControl()->SetText(code);

    ed->SetColourSet(m_Theme);
    AddEditorBase(ed);

    ed->Show(true);
    SetActiveEditor(ed);

    CodeBlocksEvent evt(cbEVT_EDITOR_OPEN, -1, nullptr, ed);
    Manager::Get()->GetPluginManager()->NotifyPlugins(evt);

    return ed;
}

void EditorManager::AddCustomEditor(EditorBase* eb)
{
    AddEditorBase(eb);
}

void EditorManager::RemoveCustomEditor(EditorBase* eb)
{
    RemoveEditorBase(eb, false);
}
/*为基础编辑器添加一个笔记本页*/
void EditorManager::AddEditorBase(EditorBase* eb)
{
    int page = FindPageFromEditor(eb);
    if (page == -1)
    {
        // use fullname as default, so tabs stay as small as possible
        // 使用全名作为默认值,因此选项卡宽度尽可能小
        wxFileName fn(eb->GetTitle());
        m_pNotebook->AddPage(eb, fn.GetFullName(), true);
    }
}

void EditorManager::RemoveEditorBase(EditorBase* eb, cb_unused bool deleteObject)
{
    int page = FindPageFromEditor(eb);
    if (page != -1 && !Manager::IsAppShuttingDown())
        m_pNotebook->RemovePage(page);

    //    if (deleteObject)
    //        eb->Destroy();
}

/*更新文件到项目文件*/
bool EditorManager::UpdateProjectFiles(cbProject* project) {
    for (size_t i = 0; i < m_pNotebook->GetPageCount(); ++i) {
        cbEditor *ed = InternalGetBuiltinEditor(i);
        /*对应的内置编辑器不存在继续*/
        if (!ed) { continue; }
        ProjectFile *pf = ed->GetProjectFile();
        /*不存在对应的项目文件(是独立文件)继续*/
        if (!pf) { continue; }
        /*文件对应的父项目不属于提供的项目继续*/
        if (pf->GetParentProject() != project) { continue; }
        /*下一个编辑器标签位置*/
        pf->editorTabPos = m_pNotebook->GetTabPositionFromIndex(i) + 1;
        ed->UpdateProjectFile();
    }
    return true;
}

bool EditorManager::CloseAll(bool dontsave)
{
    EditorBase *startPage = GetEditor(_("Start_here"));
    std::vector<EditorBase*> editors;
    editors.reserve(m_pNotebook->GetPageCount());
    for (size_t ii = 0; ii < m_pNotebook->GetPageCount(); ++ii)
    {
        EditorBase *eb = static_cast<EditorBase*>(m_pNotebook->GetPage(ii));
        if (startPage != eb)
            editors.push_back(eb);
    }
    return CloseEditors(editors, dontsave);
}

bool EditorManager::QueryCloseAll()
{
    for (int i = m_pNotebook->GetPageCount() - 1; i >= 0; --i)
    {
        EditorBase* eb = InternalGetEditorBase(i);
        // aborted
        if (eb && !QueryClose(eb))
            return false;
    }
    return true;
}

bool EditorManager::CloseAllExcept(EditorBase* editor, bool dontsave)
{
    std::vector<EditorBase*> editors;
    editors.reserve(m_pNotebook->GetPageCount());
    for (size_t ii = 0; ii < m_pNotebook->GetPageCount(); ++ii)
        editors.push_back(InternalGetEditorBase(ii));

    editors.erase(std::remove(editors.begin(), editors.end(), editor), editors.end());

    return CloseEditors(editors, dontsave);
}

bool EditorManager::CloseAllInTabCtrl(bool dontsave)
{
    std::vector<EditorBase*> editors;
    GetEditorsInTabCtrl(editors, GetActiveEditor());

    return CloseEditors(editors, dontsave);
}

bool EditorManager::CloseAllInTabCtrlExcept(EditorBase* editor, bool dontsave)
{
    std::vector<EditorBase*> editors;
    GetEditorsInTabCtrl(editors, GetActiveEditor());

    editors.erase(std::remove(editors.begin(), editors.end(), editor), editors.end());

    return CloseEditors(editors, dontsave);
}

void EditorManager::GetEditorsInTabCtrl(std::vector<EditorBase*> &editors, EditorBase *editor)
{
    std::vector<wxWindow*> windows;
    m_pNotebook->GetPagesInTabCtrl(windows, editor);
    if (!windows.empty())
    {
        editors.reserve(windows.size());
        for (std::vector<wxWindow*>::const_iterator it = windows.begin(); it != windows.end(); ++it)
            editors.push_back(static_cast<EditorBase*>(*it));
    }
}

bool EditorManager::CloseEditors(const std::vector<EditorBase*> &editors, bool dontsave)
{
    if (!dontsave)
    {
        for (std::vector<EditorBase*>::const_iterator it = editors.begin(); it != editors.end(); ++it)
        {
            EditorBase* eb = *it;
            // aborted
            if (eb && !QueryClose(eb))
                return false;
        }
    }

    m_pNotebook->Freeze();
    int count = editors.size();
    for (std::vector<EditorBase*>::const_reverse_iterator it = editors.rbegin(); it != editors.rend(); ++it)
    {
        EditorBase* eb = *it;
        if (eb && Close(eb, true))
            --count;
    }
    m_pNotebook->Thaw();
    return (count == 0);
}

bool EditorManager::CloseActive(bool dontsave)
{
    return Close(GetActiveEditor(), dontsave);
}

bool EditorManager::QueryClose(EditorBase *ed)
{
    if (!ed)
        return true;

    return ed->QueryClose();
}

int EditorManager::FindPageFromEditor(EditorBase* eb)
{
    if (!m_pNotebook || !eb)
        return -1;

    for (size_t i = 0; i < m_pNotebook->GetPageCount(); ++i)
    {
        if (m_pNotebook->GetPage(i) == eb)
            return i;
    }
    return -1;
}

bool EditorManager::Close(const wxString& filename, bool dontsave)
{
    return Close(IsOpen(filename), dontsave);
}

bool EditorManager::Close(EditorBase* editor, bool dontsave)
{
    if (editor)
    {
        int idx = FindPageFromEditor(editor);
        if (idx != -1)
        {
            if (!dontsave)
                if (!QueryClose(editor))
                    return false;
            m_pNotebook->DeletePage(idx);
        }
    }
    return true;
}

bool EditorManager::Close(int index, bool dontsave)
{
    EditorBase* ed = InternalGetEditorBase(index);
    if (ed)
        return Close(ed, dontsave);
    return false;
}

bool EditorManager::Save(const wxString& filename)
{
    EditorBase* ed = IsOpen(filename);
    if (ed)
    {
        if (!ed->Save())
            return false;
        return true;
    }
    return true;
}

bool EditorManager::Save(int index)
{
    EditorBase* ed = InternalGetEditorBase(index);
    if (ed)
    {
        if (!ed->Save())
            return false;
        return true;
    }
    return false;
}

bool EditorManager::SaveActive()
{
    EditorBase* ed = GetActiveEditor();
    if (ed)
    {
        if (!ed->Save())
            return false;
        return true;
    }
    return true;
}

bool EditorManager::SaveAs(int index)
{
    EditorBase* eb = InternalGetEditorBase(index);
    if (!eb)
        return false;
    return eb->SaveAs();
}

bool EditorManager::SaveActiveAs()
{
    EditorBase* eb = GetActiveEditor();
    if (eb)
    {
        return eb->SaveAs();
    }
    return true;
}

bool EditorManager::SaveAll()
{
    for (size_t i = 0; i < m_pNotebook->GetPageCount(); ++i)
    {
        EditorBase* ed = InternalGetEditorBase(i);
        if (ed && ed->GetModified() && !ed->Save())
        {
            wxString msg;
            msg.Printf(wxT("无法保存文件%s..."), ed->GetFilename().wx_str());
            cbMessageBox(msg, wxT("保存文件时出错"), wxICON_ERROR);
        }
    }

    return true;
}

void EditorManager::Print(PrintScope ps, PrintColourMode pcm, bool line_numbers)
{
    switch (ps)
    {
    case psAllOpenEditors:
        {
            for (size_t i = 0; i < m_pNotebook->GetPageCount(); ++i)
            {
                cbEditor* ed = InternalGetBuiltinEditor(i);
                if (ed)
                    ed->Print(false, pcm, line_numbers);
            }
            break;
        }
    case psActiveEditor: // fall through
    case psSelection:    // fall through
    default:
        {
            cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
            if (ed)
                ed->Print(ps == psSelection, pcm, line_numbers);
            break;
        }
    }
}

void EditorManager::CheckForExternallyModifiedFiles()
{
    // for some reason, a mutex locker does not work???
    // 出于某种原因,互斥锁不起作用???
    if (m_isCheckingForExternallyModifiedFiles)
        {return;}
    m_isCheckingForExternallyModifiedFiles = true;
    // flag to stop bugging the user
    // 停止对用户进行调试的标志
    bool reloadAll = false;
    // list of files failed to reload
    // 无法重新加载文件列表
    wxArrayString failedFiles;
    for (size_t i = 0; i < m_pNotebook->GetPageCount(); ++i)
    {
        cbEditor* ed = InternalGetBuiltinEditor(i);
        bool b_modified = false;

        // no builtin editor or new file not yet saved
        // 没有内置编辑器或新文件尚未保存
        if (!ed)
        {
            EditorBase* eb = InternalGetEditorBase(i);
            if (eb)
            {
                ProjectFile* pf = nullptr;

                wxFileName fn(eb->GetFilename());
                fn.MakeAbsolute();
                Manager::Get()->GetProjectManager()->FindProjectForFile(fn.GetFullPath(), &pf, false, false);

                bool readOnly = eb->IsReadOnly() || (fn.FileExists() && !wxFile::Access(fn.GetFullPath(), wxFile::write));
                MarkReadOnly(i, readOnly);

                if (pf)
                {
                    // file was deleted ?
                    // 文件被删除？
                    if (!fn.FileExists())
                    {
                        // already asked
                        // 已经问过了
                        if (pf->GetFileState() != fvsMissing)
                        {
                            // Find the window, that actually has the mouse-focus and force a release
                            // prevents crash on windows or hang on wxGTK
                            // 找到窗口,它实际上有鼠标焦点并强制释放以防止在窗口上崩溃或挂在wxGTK上
                            wxWindow* win = wxWindow::GetCapture();
                            if (win)
                                win->ReleaseMouse();

                            wxString msg;
                            msg.Printf(wxT("%s已被删除,或不再可用.\n"
                                         "是否尝试将文件保存到磁盘?\n"
                                         "如果你关闭它,它很可能会丢失!\n"
                                         "如果你取消这个对话,你必须小心!\n"
                                         "是:保存文件,否:关闭文件,取消:祈祷."), eb->GetFilename().wx_str());
                            int ret = cbMessageBox(msg, wxT("文件已更改!"), wxICON_QUESTION | wxYES_NO | wxCANCEL );
                            switch (ret)
                            {
                                case wxID_YES:
                                {
                                    eb->Save();
                                    break;
                                }
                                case wxID_NO:
                                {
                                    pf->SetFileState(fvsMissing);
                                    eb->Close();
                                    break;
                                }
                                case wxID_CANCEL: // fall through
                                default:
                                    // some editors might implement it
                                    // 一些编辑器可能会实现它
                                    eb->SetModified(true);
                                    pf->SetFileState(fvsMissing);
                                    break;
                            }
                        }
                    }
                    else
                        pf->SetFileState(readOnly?fvsReadOnly:fvsNormal);
                }
                continue;
            }
        }
        if (!ed->IsOK())
            continue;

        ProjectFile* pf = ed->GetProjectFile();
        if (pf)
        {
            cbProject* prj = pf->GetParentProject();
            if (prj && !prj->GetCheckForExternallyModifiedFiles())
                continue;
        }

        // File was deleted?
        if (!wxFileExists(ed->GetFilename()))
        {
            // Already set the flag
            // 已经设置了标志
            if (ed->GetModified())
                continue;

            // Find the window, that actually has the mouse-focus and force a release
            // prevents crash on windows or hang on wxGTK
            wxWindow* win = wxWindow::GetCapture();
            if (win)
                win->ReleaseMouse();

            wxString msg;
            msg.Printf(wxT("%s已被删除,或不再可用.\n"
                         "你想保持文件打开吗?\n"
                         "是保存文件,否关闭文件."), ed->GetFilename().wx_str());
            if (cbMessageBox(msg, wxT("文件已更改!"), wxICON_QUESTION | wxYES_NO) == wxID_YES)
                ed->SetModified(true);
            else
            {
                if (pf)
                    pf->SetFileState(fvsMissing);
                ed->Close();
            }
            continue;
        }

        wxFileName fname(ed->GetFilename());
        wxDateTime last = fname.GetModificationTime();

        //File changed from RO -> RW?
        // 文件从RO->RW更改？
        if (ed->GetControl()->GetReadOnly() &&
                wxFile::Access(ed->GetFilename().c_str(), wxFile::write))
        {
            b_modified = false;
            ed->SetReadOnly(false);
            MarkReadOnly(i, false);
            if (pf)
                pf->SetFileState(fvsNormal);
        }
        //File changed from RW -> RO?
        if (!ed->GetControl()->GetReadOnly() &&
                !wxFile::Access(ed->GetFilename().c_str(), wxFile::write))
        {
            b_modified = false;
            ed->SetReadOnly(true);
            MarkReadOnly(i);
            if (pf)
                pf->SetFileState(fvsReadOnly);
        }
        //File content changed?
        // 文件内容已更改？
        if (last.IsLaterThan(ed->GetLastModificationTime()))
            b_modified = true;

        if (b_modified)
        {
            // modified; ask to reload
            /*被修改,询问重载入*/
            int ret = -1;
            if (!reloadAll)
            {
                wxString msg;
                msg.Printf(wxT("文件%s在IDE外部被修改...\n 是否要重新加载(将丢失所有未保存的工作)?"),
                           ed->GetFilename().wx_str());
                ConfirmReplaceDlg dlg(Manager::Get()->GetAppWindow(), false, msg);
                dlg.SetTitle(wxT("重载文件?"));
                dlg.GetSizer()->SetSizeHints(&dlg);
                PlaceWindow(&dlg);

                // Find the window, that actually has the mouse-focus and force a release
                // prevents crash on windows or hang on wxGTK
                /*找到窗口,它实际上有鼠标焦点并强制释放以防止在窗口上崩溃或挂在wxGTK上*/
                wxWindow* win = wxWindow::GetCapture();
                if (win)
                    win->ReleaseMouse();

                ret = dlg.ShowModal();
                reloadAll = ret == crAll;
            }
            if (reloadAll || ret == crYes)
            {
                if (!ed->Reload())
                    failedFiles.Add(ed->GetFilename());
            }
            else if (ret == crCancel)
                break;
            else if (ret == crNo)
                ed->Touch();
        }
    }

    // this will emmit a EVT_EDITOR_ACTIVATED event, which in turn will notify
    // the app to update the currently active file's info
    // (we 're interested in updating read-write state)
    // 这将激活一个EVT_EDITOR_ACTIVATED事件,进而通知应用程序更新当前活动文件的信息(我们对更新读写状态感兴趣)
    SetActiveEditor(GetActiveEditor());

    if (failedFiles.GetCount())
    {
        // Find the window, that actually has the mouse-focus and force a release
        // prevents crash on windows or hang on wxGTK
        /*找到窗口,它实际上有鼠标焦点并强制释放以防止在窗口上崩溃或挂在wxGTK上*/
        wxWindow* win = wxWindow::GetCapture();
        if (win)
            win->ReleaseMouse();

        wxString msg;
        msg.Printf(wxT("无法重新加载所有文件:\n\n%s"), GetStringFromArray(failedFiles, _T("\n")).wx_str());
        cbMessageBox(msg,wxT("错误"), wxICON_ERROR);
    }
    m_isCheckingForExternallyModifiedFiles = false;
}

void EditorManager::MarkReadOnly(int page, bool readOnly)
{
    if (page > -1)
    {
        wxBitmap bmp = readOnly ? cbLoadBitmap(ConfigManager::GetDataFolder() + _T("/images/") + _T("readonly.png")) : wxNullBitmap;
        if (m_pNotebook)
            m_pNotebook->SetPageBitmap(page, bmp);
    }
}

/*是头文件或源文件*/
bool EditorManager::IsHeaderSource(const wxFileName& candidateFile, const wxFileName& activeFile, FileType ftActive, bool& isCandidate)
{
    // Verify the base name matches
    // 验证基本名称是否匹配
    if (candidateFile.GetName().CmpNoCase(activeFile.GetName()) == 0)
    {
        // matching case has priority over case-insensitive
        // 匹配大小写优先于不区分大小写
        isCandidate = (candidateFile.GetName() != activeFile.GetName());

        // Verify:
        // If looking for a header we have a source OR
        // If looking for a source we have a header
        // 验证:
        // 如果要找一个头文件我们有一个源文件或者如果要查找源文件,我们有一个头文件
        FileType ftTested = FileTypeOf(candidateFile.GetFullName());
        if (    ((ftActive == ftHeader)         && (ftTested == ftSource))
             || ((ftActive == ftSource)         && (ftTested == ftHeader))
             || ((ftActive == ftHeader)         && (ftTested == ftTemplateSource))
             || ((ftActive == ftTemplateSource) && (ftTested == ftHeader)) )
        {
            // Handle the case where two files (in different directories) have the same name:
            // Example: A project file with three files dir1/file.h dir1/file.cpp dir2/file.h
            // If you are in dir2/file.h and you want to swap Code::Blocks will first look if there
            // isn't a file.h in that directory, which is in this case and would then ask the user
            // to create a new file.cpp in dir2
            // Check if we are not in the same Directory
            //
            // 处理两个文件(在不同目录中)具有相同名称的情况:
            // 示例:具有三个文件dir1/file.h dir1/file.cpp dir2/file.h的项目文件
            // 如果您在dir2/file.h中,并且希望交换Code::Blocks将首先查看该目录中是否没有file.h,
            // 在本例中,这将要求用户在dir2中创建一个新的file.cpp
            // 检查我们是否在同一个目录中
            //
            if (candidateFile.GetPath() != activeFile.GetPath())
            {
                wxArrayString fileArray;
                wxDir::GetAllFiles(candidateFile.GetPath(wxPATH_GET_VOLUME), &fileArray, candidateFile.GetName() + _T(".*"), wxDIR_FILES | wxDIR_HIDDEN);
                // if in this directory there is already
                // a header file (or source file) for our candidate
                // file it can't be our candidate file
                //
                // 如果此目录中已存在候选文件的头文件(或源文件),则它不能是候选文件

                for (unsigned i=0; i<fileArray.GetCount(); i++) {
                    if (wxFileName(fileArray[i]).GetFullName() == activeFile.GetFullName()){return false;}
                }
            }
            if (candidateFile.FileExists()){return true;}
        }
    }
    return false;
}

/*查找头文件或源文件*/
wxFileName EditorManager::FindHeaderSource(const wxArrayString& candidateFilesArray, const wxFileName& activeFile, bool& isCandidate)
{
    FileType ftActive = FileTypeOf(activeFile.GetFullName());

    // Because ftActive == ftHeader || ftSource, the extension has at least 1 character
    // 因为 ftActive == ftHeader || ftSource,扩展名至少有1个字符
    bool extStartsWithCapital = wxIsupper(activeFile.GetExt()[0]);

    wxFileName candidateFile;
    for (unsigned i = 0; i < candidateFilesArray.GetCount(); ++i)
    {
        wxFileName currentCandidateFile(candidateFilesArray[i]);

        if (IsHeaderSource(currentCandidateFile, activeFile, ftActive, isCandidate))
        {
            bool isUpper = wxIsupper(currentCandidateFile.GetExt()[0]);
            if (isUpper == extStartsWithCapital && !isCandidate)
            {
                // we definitely found the header/source we were searching for
                // 我们确实找到了我们要搜索的头文件/源文件
                return currentCandidateFile;
            }
            else
            {
                // the header/source has a different capitalization of its extension
                // use this if nothing better is found
                // 头文件/源文件的扩展名大小写不同如果没有找到更好的
                candidateFile = currentCandidateFile;
            }
        }
    }

    isCandidate = true;

    // may be invalid (empty) file name
    // 可能是无效(空)文件名
    return candidateFile;
}

/*打开候选文件数据结构*/
struct OpenContainingFolderData
{
    wxString command;
    bool supportSelect;
    OpenContainingFolderData() : supportSelect(false) {}
    OpenContainingFolderData(const wxString &command, bool select) : command(command), supportSelect(select) {}
};

#if !defined(__WXMSW__) && !defined(__WXMAC__)
/// Try to detect if the file browser used by the user is nautilus (either selected by xdg-open or manually specified).
/// Newer versions of nautilus (3.0.2) support selecting files, so we modify the returned command to pass the --select
/// flag.
///
/// nautilus[鹦鹉螺]:nautilus是GNOME桌面下的一个文件管理工具.通过这个命令我们可以在终端下非常方便的打开指定目录的文件.
/// 尝试检测用户使用的文件浏览器是否为nautilus(由xdg-open选择或手动指定).
/// 更新版本的nautilus(3.0.2)支持选择文件,因此我们修改返回的命令以传递--select标志.

static OpenContainingFolderData detectNautilus(const wxString &command, ConfigManager* appConfig)
{
    wxString fileManager;

    // If the user hasn't changed the command, try to detect nautilus using xdg-mime.
    // 如果用户没有更改命令,请尝试使用xdg-mime检测nautilus.
    if (command == cbDEFAULT_OPEN_FOLDER_CMD)
    {
        const wxString shell = appConfig->Read(_T("/console_shell"), DEFAULT_CONSOLE_SHELL);
        const wxString cmdGetManager = shell + wxT(" 'xdg-mime query default inode/directory'");
        wxArrayString output, errors;
        wxExecute(cmdGetManager, output, errors, wxEXEC_SYNC);
        if (output.empty())
            return OpenContainingFolderData(command, false);
        fileManager = output[0];
    }
    else
        fileManager = command;

    Manager::Get()->GetLogManager()->DebugLog(F(wxT("文件管理器是: '%s'"), fileManager.wx_str()));
    if (fileManager.find(wxT("nautilus")) == wxString::npos)
        return OpenContainingFolderData(command, false);
    // If the file manager ends with desktop then this is produced by xdg-mime.
    // This means that we could use the system nautilus (not entirely correct).
    // 如果文件管理器以desktop结尾,则由xdg mime生成.
    // 这意味着我们可以使用鹦鹉螺系统(不完全正确).
    if (fileManager.EndsWith(wxT(".desktop")))
        fileManager = wxT("nautilus");

    wxArrayString output, errors;
    wxExecute(fileManager + wxT(" --version"), output, errors, wxEXEC_SYNC);
    if (output.empty())
        return OpenContainingFolderData(command, false);
    // It is assumed that the output looks like GNOME nautilus 3.20.4
    // 假设输出看起来像GNOME nautilus 3.20.4
    const wxString prefix(wxT("GNOME nautilus "));

    const wxString firstLine = output[0].Trim(true).Trim(false);
    Manager::Get()->GetLogManager()->DebugLog(F(wxT("鹦鹉螺文件管理器版本是: '%s'"), firstLine.wx_str()));

    if (firstLine.StartsWith(prefix))
    {
        wxArrayString versionTokens = wxStringTokenize(firstLine.substr(prefix.length()), wxT("."));
        int fullVersion = 0;
        int multiplier = 1;
        for (int ii = versionTokens.GetCount() - 1; ii >= 0; --ii)
        {
            long number;
            versionTokens[ii].ToLong(&number);
            fullVersion += number * multiplier;
            multiplier *= 100;
        }
        if (fullVersion >= 30002)
            return OpenContainingFolderData(fileManager + wxT(" --select"), true);
    }
    return OpenContainingFolderData(command, false);
}
#endif // !__WXMSW__ && !__WXMAC__

//打开包含文件夹
bool EditorManager::OpenContainingFolder()
{
    //内置编辑器
    cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
    if (!ed){return false;}

    ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("app"));
    const wxString &command = cfg->Read(_T("open_containing_folder"), cbDEFAULT_OPEN_FOLDER_CMD);
#if defined __WXMSW__ || defined __WXMAC__
    OpenContainingFolderData cmdData(command, true);
#else
    OpenContainingFolderData cmdData=detectNautilus(command, cfg);
#endif

    const wxString& fullPath = ed->GetFilename();
    cmdData.command << wxT(" ");
    if (!cmdData.supportSelect)
    {
        // Cannot select the file with with most editors, so just extract the folder name
        // 无法使用大多数编辑器选择文件,因此只需提取文件夹名称
        wxString splitPath;
        wxFileName::SplitPath(fullPath, &splitPath, nullptr, nullptr);
        cmdData.command << splitPath;
    }
    else
        cmdData.command << fullPath;

    wxExecute(cmdData.command);
    Manager::Get()->GetLogManager()->DebugLog(F(wxT("执行命令打开文件夹: '%s'"),cmdData.command.wx_str()));
    return true;
}

/*头文件与源文件互切*/
bool EditorManager::SwapActiveHeaderSource()
{
    cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
    if (!ed){return false;}

    ProjectManager *pm = Manager::Get()->GetProjectManager();
    if (!pm){return false;}

    FileType ft = FileTypeOf(ed->GetFilename());
    if (ft != ftHeader && ft != ftSource && ft != ftTemplateSource){return false;}

    cbProject* project = nullptr;

    // if the file in question belongs to a different open project,
    // then use that project instead.
    // this fixes locating the file's pair in a workspace when the active file
    // does not belong to the active project.
    //
    // 如果所讨论的文件属于其他打开的项目,则改用该项目.
    // 当活动文件不属于活动项目时,这将修复在工作区中定位文件对的问题.

    ProjectFile* opf = ed->GetProjectFile();
    if (opf){project = opf->GetParentProject();}

    // if we didn't get a valid project, try the active one
    // 如果我们没有得到一个有效的项目,请尝试活动的项目
    if (!project){project = pm->GetActiveProject();}

    wxFileName theFile(ed->GetFilename());
    wxFileName candidateFile;
    bool isCandidate;
    wxArrayString fileArray;

    // find all files with the same name as the active file, but with possibly different extension
    // search in the directory of the active file:
    // 查找与活动文件同名但可能具有不同扩展名的所有文件,在活动文件的目录中搜索:
    wxDir::GetAllFiles(theFile.GetPath(wxPATH_GET_VOLUME), &fileArray, theFile.GetName() + _T(".*"), wxDIR_FILES | wxDIR_HIDDEN);

    // try to find the header/source in the list
    // 尝试在列表中查找头文件/源文件
    wxFileName currentCandidateFile = FindHeaderSource(fileArray, theFile, isCandidate);

    if (isCandidate)
    {
        candidateFile = currentCandidateFile;
    }
    else if (currentCandidateFile.IsOk())
    {
        cbEditor* newEd = Open(currentCandidateFile.GetFullPath());
        // we found and were able to open it
        // --> RETURN;
        // 我们找到了并打开了它
        if (newEd!=nullptr){return true;}
    }

    // try to find the file among the opened files
    // 尝试在打开的文件中查找该文件

    // build a list of opened files
    // 生成打开文件的列表
    fileArray.Clear();
    for (int i = 0; i < GetEditorsCount(); ++i)
    {
        cbEditor* edit = GetBuiltinEditor(GetEditor(i));
        if (!edit){continue;}

        ProjectFile* pf = edit->GetProjectFile();
        if (!pf){continue;}

        fileArray.Add(pf->file.GetFullPath());
    }

    // try to find the header/source in the list
    // 尝试在列表中查找头文件/源文件
    currentCandidateFile = FindHeaderSource(fileArray, theFile, isCandidate);

    if (!isCandidate && currentCandidateFile.IsOk())
    {
        cbEditor* newEd = Open(currentCandidateFile.GetFullPath());
        // we found and were able to open it
        // 我们找到了并打开了它
        // --> RETURN;
        if (newEd!=nullptr){return true;}
    }

    if (project)
    {
        // try to find in the project files
        // 尝试在项目文件中查找
        // build a list of project files
        // 生成打开文件的列表
        fileArray.Clear();
        for (FilesList::iterator it = project->GetFilesList().begin(); it != project->GetFilesList().end(); ++it)
        {
            ProjectFile* pf = *it;
            if (!pf){continue;}
            fileArray.Add(pf->file.GetFullPath());
        }

        // try to find the header/source in the list
        // 尝试在列表中查找头文件/源文件
        currentCandidateFile = FindHeaderSource(fileArray, theFile, isCandidate);

        if (isCandidate && !candidateFile.IsOk())
        {
            candidateFile = currentCandidateFile;
        }
        else if (currentCandidateFile.IsOk())
        {
            cbEditor* newEd = Open(currentCandidateFile.GetFullPath());
            // we found and were able to open it
            // 我们找到了并打开了它
            // --> RETURN;
            if (newEd!=nullptr){return true;}
        }

        // if not found, build the list of directories for further searching
        // 如果找不到,则生成目录列表以供进一步搜索

        // get project's include dirs
        // 获取项目的包含目录

        wxArrayString dirs = project->GetIncludeDirs();

        // get targets include dirs
        // 获取目标的包含目录
        for (int i = 0; i < project->GetBuildTargetsCount(); ++i)
        {
            ProjectBuildTarget* target = project->GetBuildTarget(i);
            if (target)
            {
                for (unsigned int ti = 0; ti < target->GetIncludeDirs().GetCount(); ++ti)
                {
                    // TODO (Morten#5#): target include dirs might override project include dirs, take append/prepend option into account
                    // 待处理(Morten#5#):目标包含目录可能覆盖项目包含目录,请考虑追加/前置选项
                    wxString dir = target->GetIncludeDirs()[ti];
                    if (dirs.Index(dir) == wxNOT_FOUND){dirs.Add(dir);}
                }
            }
        }

        // go through the directories and try to find the header/source there
        // 浏览目录并尝试在那里找到头文件/源文件
        for (unsigned int i = 0; i < dirs.GetCount(); ++i)
        {
            // might contain macros -> replace them
            // 可能包含宏->替换它们
            wxString dir = dirs[i];
            Manager::Get()->GetMacrosManager()->ReplaceMacros(dir);

            wxFileName dname(dir);
            if (!dname.IsAbsolute())
            {
                dname.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, project->GetBasePath());
            }

            fileArray.Clear();
            // find all files inside the directory with the same name as the active file, but with possibly different extension
            // 查找目录中与活动文件同名但可能具有不同扩展名的所有文件
            wxDir::GetAllFiles(dname.GetPath(), &fileArray, theFile.GetName() + _T(".*"), wxDIR_FILES | wxDIR_HIDDEN);
            // try to find the header/source in the list
            // 尝试在列表中查找头文件/源文件
            currentCandidateFile = FindHeaderSource(fileArray, theFile, isCandidate);

            if (isCandidate)
            {
                candidateFile = currentCandidateFile;
            }
            else if (currentCandidateFile.IsOk()) {
                cbEditor *newEd = Open(currentCandidateFile.GetFullPath());
                // we found and were able to open it
                // 我们找到了并打开了它
                // --> RETURN;
                if (newEd != nullptr) { return true; }
            }
        }
    }

    // candidateFile is header/source whose extension does not match the capitalization of the active file
    // - open it if nothing better is found
    /*candidateFile是扩展名与活动文件大小写不匹配的头文件/源文件 -如果没有更好的发现就打开它*/
    if (candidateFile.IsOk()) {
        cbEditor *newEd = Open(candidateFile.GetFullPath());
        // we found and were able to open it --> RETURN;
        /*我们找到了并打开了它*/
        if (newEd != nullptr) { return true; }
    }

    // nothing else worked, try to find source-source pairs of auto-generated files
    /*没有其他工作,请尝试查找自动生成的文件的源-源对*/
    if (opf) {
        ProjectFile *candidatePF = opf->AutoGeneratedBy();
        if (!candidatePF && !opf->generatedFiles.empty()) { candidatePF = opf->generatedFiles.front(); }
        if (candidatePF) {
            cbEditor *newEd = Open(candidatePF->file.GetFullPath());
            if (newEd != nullptr) { return true; }
        }
    }

    // We couldn't find the file, maybe it does not exist. Ask the user if we should create it:
    // 我们找不到文件,可能它不存在.询问用户是否应该创建它：
    if (cbMessageBox(wxT("文件似乎不存在.你想创建它吗?"),
                wxT("错误"), wxICON_QUESTION | wxYES_NO) == wxID_YES) {
        project = Manager::Get()->GetProjectManager()->GetActiveProject();
        if (project)
            wxSetWorkingDirectory(project->GetBasePath());

        // Create a suggestion for the new file name:
        // 为新文件名创建建议：
        if (ft == ftHeader)
            theFile.SetExt(FileFilters::CPP_EXT);
        else if (ft == ftSource || ft == ftTemplateSource)
            theFile.SetExt(FileFilters::H_EXT);
        // else? Well, if the filename is not changed we could possibly
        // overwrite an existing file with our suggestion.
        /*还有吗？好吧,如果文件名没有改变,我们可以用我们的建议覆盖现有的文件.*/

        cbEditor *newEd = New(theFile.GetFullPath());
        if (project) {
            if (cbMessageBox(_("是否要将此新文件添加到活动项目中?"),
                             wxT("将文件添加到项目"),
                             wxYES_NO | wxICON_QUESTION) == wxID_YES) {
                wxArrayInt targets;
                if (Manager::Get()->GetProjectManager()->AddFileToProject(newEd->GetFilename(), project, targets) != 0) {
                    ProjectFile *pf = project->GetFileByFilename(newEd->GetFilename(), false);
                    newEd->SetProjectFile(pf);
                    Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
                }
            }
        }

        // verify that the open files are still in sync
        // the new file might have overwritten an existing one)
        /*验证打开的文件是否仍处于同步状态新文件可能已覆盖现有文件*/
        CheckForExternallyModifiedFiles();
    }

    return false;
}
/*通用上下文菜单响应事件处理程序*/
void EditorManager::OnGenericContextMenuHandler(wxCommandEvent& event)
{
    EditorBase* eb = GetActiveEditor();
    cbEditor* ed = GetBuiltinEditor(eb);
    int id = event.GetId();

    if (id == idNBTabSplitHorz && ed)
        ed->Split(cbEditor::stHorizontal);
    else if (id == idNBTabSplitVert && ed)
        ed->Split(cbEditor::stVertical);
    else if (id == idNBTabUnsplit && ed)
        ed->Unsplit();
    else if (id >= idNBSwitchFile1 && id <= idNBSwitchFileMax)
    {
        eb = GetEditor(id - idNBSwitchFile1);
        if (eb)
            SetActiveEditor(eb);
    }
}
/*标签页发生变化*/
void EditorManager::OnPageChanged(wxAuiNotebookEvent& event)
{
    EditorBase* eb = static_cast<EditorBase*>(m_pNotebook->GetPage(event.GetSelection()));
    EditorBase* eb_old = nullptr;
    if (event.GetOldSelection()!=-1)
        eb_old = static_cast<EditorBase*>(m_pNotebook->GetPage(event.GetOldSelection()));

    CodeBlocksEvent evt(cbEVT_EDITOR_SWITCHED, -1, nullptr, eb, nullptr, eb_old);
    Manager::Get()->GetPluginManager()->NotifyPlugins(evt);

    CodeBlocksEvent evt2(cbEVT_EDITOR_ACTIVATED, -1, nullptr, eb);
    Manager::Get()->GetPluginManager()->NotifyPlugins(evt2);

    if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/tabs_stacked_based_switching")))
    {
        wxWindow*        wnd;
        cbNotebookStack* body;
        cbNotebookStack* tmp;
        wnd = m_pNotebook->GetPage(event.GetSelection());
        for (body = m_pNotebookStackHead; body->next != nullptr; body = body->next)
        {
            if (wnd == body->next->window)
            {
                if (m_pNotebookStackTail == body->next)
                    m_pNotebookStackTail = body;
                tmp = body->next;
                body->next = tmp->next;
                tmp->next = m_pNotebookStackHead->next;
                m_pNotebookStackHead->next = tmp;
                break;
            }
        }
        if (   (m_pNotebookStackHead->next == nullptr)
            || (wnd != m_pNotebookStackHead->next->window) )
        {
            body = new cbNotebookStack(wnd);
            body->next = m_pNotebookStackHead->next;
            m_pNotebookStackHead->next = body;
            ++m_nNotebookStackSize;
        }
    }

    // focus editor on next update event
    /*聚焦编辑器到下一个更新事件*/
    m_pData->m_SetFocusFlag = true;
    // allow others to process it too
    /*允许其他人也处理它(放弃独占)*/
    event.Skip();
}

void EditorManager::OnPageChanging(wxAuiNotebookEvent& event)
{
    int old_sel = event.GetOldSelection();
    if (old_sel != -1 && static_cast<size_t>(old_sel) < m_pNotebook->GetPageCount())
    {
        EditorBase* eb = static_cast<EditorBase*>(m_pNotebook->GetPage(old_sel));
        CodeBlocksEvent evt(cbEVT_EDITOR_DEACTIVATED, -1, nullptr, eb);
        Manager::Get()->GetPluginManager()->NotifyPlugins(evt);
    }
    event.Skip();
}

void EditorManager::OnPageClose(wxAuiNotebookEvent& event)
{
    int sel = event.GetSelection();
    bool doClose = false;
    EditorBase* eb = nullptr;
    if (sel != -1)
    {
        // veto it in any case, so we can handle the page delete or remove ourselves
        /*在任何情况下都可以否决它,这样我们就可以处理页面删除或自己删除*/
        event.Veto();
        eb = static_cast<EditorBase*>(m_pNotebook->GetPage(sel));
        if (QueryClose(eb))
        {
            doClose = true;
            if (m_pNotebook->GetPageCount()<=1)
            {
                CodeBlocksEvent evt(cbEVT_EDITOR_SWITCHED, -1, nullptr, nullptr, nullptr, eb);
                Manager::Get()->GetPluginManager()->NotifyPlugins(evt);
            }
        }
    }

    if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/tabs_stacked_based_switching")))
    {
        wxWindow* wnd;
        cbNotebookStack* body;
        cbNotebookStack* tmp;
        wnd = m_pNotebook->GetPage(event.GetSelection());
        for (body = m_pNotebookStackHead; body->next != nullptr; body = body->next)
        {
            if (wnd == body->next->window)
            {
                tmp = body->next;
                body->next = tmp->next;
                delete tmp;
                --m_nNotebookStackSize;
                break;
            }
        }
    }

    if (doClose && eb != nullptr)
        Close(eb);
    else
        event.Skip();
}
/*标签页上的上下文菜单*/
void EditorManager::OnPageContextMenu(wxAuiNotebookEvent& event)
{
    if (event.GetSelection() == -1)
        return;

    if (wxGetKeyState(WXK_CONTROL) && GetEditorsCount() > 1)
    {
        wxMenu* pop = new wxMenu;
        EditorBase* current = GetActiveEditor();
        for (int i = 0; i < EditorMaxSwitchTo && i < GetEditorsCount(); ++i)
        {
            EditorBase* other = GetEditor(i);
            if (!other)
                continue;
            const wxString name = (other->GetModified() ? wxT("*") : wxEmptyString) + other->GetShortName();
            if (other == current)
            {
                // do nothing if the current tab is selected and mark it as active
                /*如果选中当前选项卡并将其标记为活动,则不执行任何操作*/
                pop->AppendCheckItem(wxID_ANY, name);
                pop->FindItemByPosition(pop->GetMenuItemCount() - 1)->Check();
            }
            else
                pop->Append(idNBSwitchFile1 + i, name);
        }
        m_pNotebook->PopupMenu(pop);
        delete pop;
        return;
    }

    // select the notebook that sends the event, because the context menu-entries act on the actual selected tab
    /*选择发送事件的笔记本,因为上下文菜单项作用于实际选定的选项卡*/
    m_pNotebook->SetSelection(event.GetSelection());
    wxMenu* pop = new wxMenu;
    pop->Append(idNBTabClose, wxT("关闭"));
    pop->Append(idNBTabCloseAll, wxT("关闭所有"));
    pop->Append(idNBTabCloseAllOthers, wxT("关闭其他"));

    wxAuiTabCtrl *activeTabCtrl = m_pNotebook->GetTabCtrl(GetActiveEditor());
    if (!activeTabCtrl || activeTabCtrl->GetPageCount() <= 1)
    {
        pop->Enable(idNBTabCloseAll, false);
        pop->Enable(idNBTabCloseAllOthers, false);
    }

    int any_modified = 0;
    for (int i = 0; i<GetEditorsCount(); ++i)
    {
        EditorBase* ed = GetEditor(i);
        if (ed && ed->GetModified()) {
            // more than one editor is modified -> enable "Save all"
            /*修改多个编辑器->启用“全部保存”*/
            if (++any_modified > 1) { break; }
        }
    }

    pop->AppendSeparator();
    pop->Append(idNBTabSave, wxT("保存"));
    pop->Append(idNBTabSaveAll, wxT("保存所有"));
    pop->Enable(idNBTabSave, false);
    pop->Enable(idNBTabSaveAll, false);

    if (any_modified > 0)
    {
        if (GetEditor(event.GetSelection())->GetModified())
            pop->Enable(idNBTabSave, true);
        if (any_modified > 1 || !GetEditor(event.GetSelection())->GetModified())
            pop->Enable(idNBTabSaveAll, true);
    }

    pop->AppendSeparator();
    cbEditor* ed = GetBuiltinEditor(event.GetSelection());
    if (ed)
    {
        pop->Append(idNBSwapHeaderSource, wxT("切换头文件与源代码"));
        pop->Append(idNBTabOpenContainingFolder, wxT("打开包含文件夹"));
        pop->Append(idNBProperties, wxT("属性..."));
        pop->AppendSeparator();
    }

    if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/editor_tabs_bottom"), false))
        pop->Append(idNBTabTop, wxT("标签在顶部"));
    else
        pop->Append(idNBTabBottom, wxT("标签在底部"));

    if (ed)
    {
        wxMenu* splitMenu = new wxMenu;
        if (ed->GetSplitType() != cbEditor::stHorizontal)
            splitMenu->Append(idNBTabSplitHorz, wxT("水平(上下)拆分"));
        if (ed->GetSplitType() != cbEditor::stVertical)
            splitMenu->Append(idNBTabSplitVert, wxT("垂直(左右)拆分"));
        if (ed->GetSplitType() != cbEditor::stNoSplit)
        {
            splitMenu->AppendSeparator();
            splitMenu->Append(idNBTabUnsplit, wxT("取消拆分"));
        }
        pop->Append(-1, wxT("拆分视图"), splitMenu);

        if (Manager::Get()->GetProjectManager()->GetActiveProject()) // project must be open
        {
            pop->AppendSeparator();

            ProjectFile *projectFile = ed->GetProjectFile();
            if (projectFile)
            {
                pop->Append(idNBRemoveFileFromProject, wxT("从项目中删除文件"));
                pop->Append(idNBShowFileInTree, wxT("在项目树中显示文件"));
            }
            else
                pop->Append(idNBAddFileToProject, wxT("将文件添加到活动项目"));
        }
    }

    // allow plugins to use this menu
    /*允许插件使用此菜单*/
    Manager::Get()->GetPluginManager()->AskPluginsForModuleMenu(mtEditorTab, pop);

    m_pNotebook->PopupMenu(pop);
    delete pop;
}

void EditorManager::OnClose(cb_unused wxCommandEvent& event)
{
    Close(GetActiveEditor());
}

void EditorManager::OnCloseAll(cb_unused wxCommandEvent& event)
{
    CloseAllInTabCtrl();
}

void EditorManager::OnCloseAllOthers(cb_unused wxCommandEvent& event)
{
    CloseAllInTabCtrlExcept(GetActiveEditor());
}

void EditorManager::OnSave(cb_unused wxCommandEvent& event)
{
    Save(m_pNotebook->GetSelection());
}

void EditorManager::OnSaveAll(cb_unused wxCommandEvent& event)
{
    SaveAll();
}

void EditorManager::OnSwapHeaderSource(cb_unused wxCommandEvent& event)
{
    SwapActiveHeaderSource();
}

void EditorManager::OnOpenContainingFolder(cb_unused wxCommandEvent& event)
{
    OpenContainingFolder();
}

void EditorManager::OnTabPosition(wxCommandEvent& event)
{
    long style = m_pNotebook->GetWindowStyleFlag();
    style &= ~wxAUI_NB_BOTTOM;

    if (event.GetId() == idNBTabBottom)
        style |= wxAUI_NB_BOTTOM;
    m_pNotebook->SetWindowStyleFlag(style);
    m_pNotebook->Refresh();

    // (style & wxAUI_NB_BOTTOM) saves info only about the the tabs position
    /*(style & wxAUI_NB_BOTTOM)仅保存有关选项卡位置的信息*/
    Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/environment/editor_tabs_bottom"),(bool)(style & wxAUI_NB_BOTTOM));
}

void EditorManager::OnProperties(cb_unused wxCommandEvent& event)
{
    cbEditor* ed = GetBuiltinActiveEditor();
    ProjectFile* pf = nullptr;
    if (ed)
        {pf = ed->GetProjectFile();}
    if (pf)
        {pf->ShowOptions(Manager::Get()->GetAppWindow());}
    else
    {
        ProjectFileOptionsDlg dlg(Manager::Get()->GetAppWindow(), GetActiveEditor()->GetFilename());
        PlaceWindow(&dlg);
        dlg.ShowModal();
    }
}

void EditorManager::OnAddFileToProject(cb_unused wxCommandEvent& event)
{
    cbProject *project = Manager::Get()->GetProjectManager()->GetActiveProject();
    wxString fname = GetBuiltinActiveEditor()->GetFilename();

    wxArrayInt targets;
    if (Manager::Get()->GetProjectManager()->AddFileToProject(fname, project, targets) != 0)
    {
        ProjectFile* pf = project->GetFileByFilename(fname, false);
        GetBuiltinActiveEditor()->SetProjectFile(pf);
        Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
    }
}

void EditorManager::OnRemoveFileFromProject(cb_unused wxCommandEvent& event)
{
    ProjectFile* pf = GetBuiltinActiveEditor()->GetProjectFile();
    // should be in any case, otherwise something went wrong between popup menu creation and here
    /*在任何情况下都应该是这样,否则在弹出菜单创建和此处之间出现了问题*/
    if (pf)
    {
        cbProject *project = pf->GetParentProject();
        Manager::Get()->GetProjectManager()->RemoveFileFromProject(pf, project);
        Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
    }
}

void EditorManager::OnShowFileInTree(cb_unused wxCommandEvent& event)
{
    ProjectFile* pf = GetBuiltinActiveEditor()->GetProjectFile();
    if (pf)
    {
        cbProjectManagerUI &ui = Manager::Get()->GetProjectManager()->GetUI();
        ui.SwitchToProjectsPage();
        ui.ShowFileInTree(*pf);
    }
}

void EditorManager::OnAppDoneStartup(wxCommandEvent& event)
{
    event.Skip();
}

void EditorManager::OnAppStartShutdown(wxCommandEvent& event)
{
    event.Skip();
}

void EditorManager::OnCheckForModifiedFiles(cb_unused wxCommandEvent& event)
{
    CheckForExternallyModifiedFiles();
}

void EditorManager::HideNotebook()
{
    if (m_pNotebook)
        m_pNotebook->Hide();
}

void EditorManager::ShowNotebook()
{
    if (m_pNotebook)
        m_pNotebook->Show();
}

void EditorManager::OnUpdateUI(wxUpdateUIEvent& event)
{
    /* Don't process UpdateUI event when the app is closing.
     * It may crash C::B */
    /*当应用程序关闭时不处理UpdateUI事件.它可能会使C::B崩溃*/
    if (!Manager::Get()->IsAppShuttingDown() && m_pData->m_SetFocusFlag)
    {
        cbEditor* ed = GetBuiltinActiveEditor();
        if (ed)
            ed->GetControl()->SetFocus();
        m_pData->m_SetFocusFlag = false;
    }

    // allow other UpdateUI handlers to process this event *very* important! don't forget it...
    // 允许其他UpdateUI处理程序处理此事件非常重要!别忘了...
    event.Skip();
}
/*采集定义*/
void EditorManager::CollectDefines(CodeBlocksEvent& event)
{
    cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
    if (   !prj
        || !Manager::Get()->GetConfigManager(wxT("editor"))->ReadBool(wxT("/track_preprocessor"),  true)
        || !Manager::Get()->GetConfigManager(wxT("editor"))->ReadBool(wxT("/collect_prj_defines"), true) )
    {
        event.Skip();
        return;
    }

    wxArrayString compilerFlags = prj->GetCompilerOptions();
    ProjectBuildTarget* tgt = prj->GetBuildTarget(prj->GetActiveBuildTarget());
    FilesList* lst;
    wxString id;
    if (tgt)
    {
        AppendArray(tgt->GetCompilerOptions(), compilerFlags);
        lst = &tgt->GetFilesList();
        id  = tgt->GetCompilerID();
    }
    else
    {
        lst = &prj->GetFilesList();
        id  = prj->GetCompilerID();
    }
    // get global flags
    /*获取全局标记*/
    Compiler* comp = CompilerFactory::GetCompiler(id);
    if (comp)
        AppendArray(comp->GetCompilerOptions(), compilerFlags);

    wxArrayString defines;
    for (size_t i = 0; i < compilerFlags.GetCount(); ++i)
    {
        if (   compilerFlags[i].StartsWith(wxT("-D"))
            || compilerFlags[i].StartsWith(wxT("/D")) )
        {
            defines.Add(compilerFlags[i].Mid(2));
        }
        else if (compilerFlags[i].Find(wxT("`")) != wxNOT_FOUND)
        {
            wxString str = compilerFlags[i];
            ExpandBackticks(str);
            // remove any leftover backticks to prevent an infinite loop
            /*删除所有剩余的反勾号以防止无限循环*/
            str.Replace(wxT("`"), wxT(" "));
            AppendArray(GetArrayFromString(str, wxT(" ")), compilerFlags);
        }
        else if (   compilerFlags[i] == wxT("-ansi")
                 || compilerFlags[i] == wxT("-std=c90")
                 || compilerFlags[i] == wxT("-std=c++98"))
        {
            defines.Add(wxT("__STRICT_ANSI__"));
        }
    }

    defines.Add(wxT("__cplusplus"));
    for (FilesList::iterator it = lst->begin(); it != lst->end(); ++it)
    {
        if ((*it)->relativeFilename.EndsWith(wxT(".c")))
        {
            // do not define '__cplusplus' if even a single C file is found
            /*即使找到一个C文件,也不要定义'__cplusplus'*/
            defines.RemoveAt(defines.GetCount() - 1);
            break;
        }
    }

    if (id.Find(wxT("gcc")) != wxNOT_FOUND)
    {
        defines.Add(wxT("__GNUC__"));
        defines.Add(wxT("__GNUG__"));
    }
    else if (id.Find(wxT("msvc")) != wxNOT_FOUND)
    {
        defines.Add(wxT("_MSC_VER"));
        defines.Add(wxT("__VISUALC__"));
    }

    if (Manager::Get()->GetConfigManager(wxT("editor"))->ReadBool(wxT("/platform_defines"), false))
    {
        if (platform::windows)
        {
            defines.Add(wxT("_WIN32"));
            defines.Add(wxT("__WIN32"));
            defines.Add(wxT("__WIN32__"));
            defines.Add(wxT("WIN32"));
            defines.Add(wxT("__WINNT"));
            defines.Add(wxT("__WINNT__"));
            defines.Add(wxT("WINNT"));
            defines.Add(wxT("__WXMSW__"));
            defines.Add(wxT("__WINDOWS__"));
            if (platform::bits == 64)
            {
                defines.Add(wxT("_WIN64"));
                defines.Add(wxT("__WIN64__"));
            }
        }
        else if (platform::macosx)
        {
            defines.Add(wxT("__WXMAC__"));
            defines.Add(wxT("__WXOSX__"));
            defines.Add(wxT("__WXCOCOA__"));
            defines.Add(wxT("__WXOSX_MAC__"));
            defines.Add(wxT("__APPLE__"));
        }
        else if (platform::Linux)
        {
            defines.Add(wxT("LINUX"));
            defines.Add(wxT("linux"));
            defines.Add(wxT("__linux"));
            defines.Add(wxT("__linux__"));
        }
        else if (platform::freebsd)
        {
            defines.Add(wxT("FREEBSD"));
            defines.Add(wxT("__FREEBSD__"));
        }
        else if (platform::netbsd)
        {
            defines.Add(wxT("NETBSD"));
            defines.Add(wxT("__NETBSD__"));
        }
        else if (platform::openbsd)
        {
            defines.Add(wxT("OPENBSD"));
            defines.Add(wxT("__OPENBSD__"));
        }
        else if (platform::darwin)
        {
            defines.Add(wxT("DARWIN"));
            defines.Add(wxT("__APPLE__"));
        }
        else if (platform::solaris)
        {
            defines.Add(wxT("sun"));
            defines.Add(wxT("__sun"));
            defines.Add(wxT("__SUN__"));
            defines.Add(wxT("__SUNOS__"));
            defines.Add(wxT("__SOLARIS__"));
        }
        if (platform::Unix)
        {
            defines.Add(wxT("unix"));
            defines.Add(wxT("__unix"));
            defines.Add(wxT("__unix__"));
            defines.Add(wxT("__UNIX__"));
        }
        if (platform::gtk)
            defines.Add(wxT("__WXGTK__"));
        if (platform::bits == 32)
        {
            defines.Add(wxT("i386"));
            defines.Add(wxT("__i386"));
            defines.Add(wxT("__i386__"));
            defines.Add(wxT("__i386__"));
            defines.Add(wxT("_X86_"));
            defines.Add(wxT("__INTEL__"));
        }
        else if (platform::bits == 64)
        {
            defines.Add(wxT("__amd64"));
            defines.Add(wxT("__amd64__"));
            defines.Add(wxT("__x86_64"));
            defines.Add(wxT("__x86_64__"));
            defines.Add(wxT("__IA64__"));
        }
    }

    const wxString keywords = GetStringFromArray(MakeUniqueArray(defines, true), wxT(" "), false);
    const HighlightLanguage hlCpp = m_Theme->GetHighlightLanguage(wxT("C/C++"));
    if (m_Theme->GetKeywords(hlCpp, 4) == keywords)
        return; // no change

    m_Theme->SetKeywords(hlCpp, 4, keywords);
    const wxString key = wxT("/colour_sets/") + m_Theme->GetName() + wxT("/cc/");
    Manager::Get()->GetConfigManager(wxT("editor"))->Write(key + wxT("editor/keywords/set4"), keywords);
    Manager::Get()->GetConfigManager(wxT("editor"))->Write(key + wxT("name"), wxT("C/C++"));

    // update open editors
    /*更新打开的编辑器*/
    for (int index = 0; index < GetEditorsCount(); ++index)
    {
        cbEditor* ed = GetBuiltinEditor(index);
        if ( ed && (ed->GetLanguage() == hlCpp) )
        {
            cbStyledTextCtrl* stc = ed->GetControl();
            stc->SetKeyWords(4, keywords);
        }
    }
    event.Skip();
}

void EditorManager::SetZoom(int zoom)
{
    m_Zoom = zoom;
}

int EditorManager::GetZoom() const
{
    return m_Zoom;
}

